home *** CD-ROM | disk | FTP | other *** search
- //////////
- //
- // File: AIFFWriter.c
- //
- // Contains: Code for dispatching a sound hardware output ('sdev') component.
- //
- // Written by: Mark Cookson, Apple Developer Technical Support
- // Based on code by Kip Olson.
- // Revised by: Tim Monroe
- //
- // Copyright: © 1993-1999 by Apple Computer, Inc., all rights reserved.
- //
- // Change History (most recent first):
- //
- // <2> 03/05/99 rtm further work to bring into line with existing component sample code
- // <1> 02/25/99 rtm first inherited file; changes to support Windows compiles
- //
- // You may incorporate this sample code into your applications without
- // restriction, though the sample code has been provided "AS IS" and the
- // responsibility for its operation is 100% yours. However, what you are
- // not permitted to do is to redistribute the source as "Apple Sample
- // Code" after having made changes. If you're going to re-distribute the
- // source, we require that you make it clear in the source that the code
- // was descended from Apple Sample Code, but that you've made changes.
- //
- //////////
-
-
- //////////
- //
- // header files
- //
- //////////
-
- #include "AIFFWriter.h"
-
-
- ///////////////////////////////////////////////////////////////////////////////////////////////////////////
- //
- // Component dispatch helper defines
- //
- ///////////////////////////////////////////////////////////////////////////////////////////////////////////
-
- #if TARGET_CPU_68K
- #define COMPONENT_C_DISPATCHER
- #define COMPONENT_DISPATCH_MAIN
- #endif
-
- #define SOUNDCOMPONENT_BASENAME() __SoundOutput
- #define SOUNDCOMPONENT_GLOBALS() SoundOutputGlobalsPtr storage
-
- #define CALLCOMPONENT_BASENAME() SOUNDCOMPONENT_BASENAME()
- #define CALLCOMPONENT_GLOBALS() SOUNDCOMPONENT_GLOBALS()
-
- #define COMPONENT_UPP_SELECT_ROOT() SoundComponent
- #define COMPONENT_DISPATCH_FILE "SoundOutputDispatch.h"
-
- #define GET_DELEGATE_COMPONENT() (storage->sourceComponent)
-
- #include "Components.k.h"
- #include "Sound.k.h"
- #include "ComponentDispatchHelper.c"
-
-
- // It is possible to debug this component using an application. To do this, you cannot call the Time
- // Manager to generate interrupts, since source level debuggers are not re-entrant. This means you must call
- // the FakeInterrupt routine described below during main event loop time and define the gGlobals variable.
-
- #ifdef FakeInterrupts
- SoundOutputGlobalsPtr gGlobals;
- #endif
-
-
- ///////////////////////////////////////////////////////////////////////////////////////////////////////////
- //
- // Required component calls
- //
- ///////////////////////////////////////////////////////////////////////////////////////////////////////////
-
- /* SoundOutputOpen
-
- This routine is called when the Component Manager creates an instance of this
- component. The routine should allocate global variables in the appropriate heap
- and call SetComponentInstanceStorage() so the Component Manager can remember
- the globals and pass them to all the method calls.
-
- Determining the heap to use can be tricky. The Component Manager will normally
- load the component code into the system heap, which is good, since many applications
- will be sharing this component to play sound. In this case, the component's global
- variable storage should also be created in the system heap.
-
- However, if system heap memory is tight, the Component Manager will load
- the component into the application heap of the first application that plays sound.
- When this happens, the component should create global storage in the application heap
- instead. The Sound Manager will make sure that other applications will not try
- to play sound while the component is in this application heap.
-
- To determine the proper heap to use, call GetComponentInstanceA5(). If the value
- returned is 0, then the component was loaded into the system heap, and all storage
- should be allocated there. If the value returned is non-zero, the component is in
- the application heap specified by returned A5 value, and all storage should be
- allocated in this application heap.
-
- NOTE: If the component is loaded into the application heap, the value returned by
- GetComponentRefCon() will be 0.
- NOTE: Do not attempt to initialize or access hardware in this call, since the
- Component Manager will call SoundOutputOpen() BEFORE calling RegisterComponent().
- Instead, initialize the hardware during InitOutputDevice(), described below.
- NOTE: This routine is never called at interrupt time.
- */
-
- PASCAL_RTN ComponentResult __SoundOutputOpen (SoundOutputGlobalsPtr globals, ComponentInstance self)
- {
- OSErr result;
-
- // get space for globals in appropriate heap
- globals = (SoundOutputGlobalsPtr)NewPtrSysClear(sizeof(SoundOutputGlobals));
- FailWithAction(globals == nil, result = MemError(), Failure);
-
- // If a component is loaded into an application's heap, then it gets an A5 value.
- // This also means that the component's refCon is 0. All of this means we need
- // to go and get the preferences just like we're being registered for the first time.
-
- if (GetComponentInstanceA5(self) != 0)
- {
- globals->inAppHeap = true;
- result = GetPreferences(globals);
- FailIf(result != noErr, NoPrefs);
- }
-
- globals->self = self;
- globals->prefs = (HardwarePrefsHandle)GetComponentRefcon((Component)self);
-
-
- #ifdef FakeInterrupts
- gGlobals = globals;
- #endif
-
- SetComponentInstanceStorage(self, (Handle) globals); // save pointer to our globals
- return (noErr);
-
- NoPrefs:
- DisposePtr((Ptr)globals);
- Failure:
- return (result);
- }
-
-
- /* SoundOutputClose
-
- This routine is called when the Component Manager is closing the instance of
- this component. The routine should make sure all remaining data is written
- to the hardware and that the hardware is completely turned off. It should
- delete all global storage and close any other components that were opened.
-
- NOTE: Be sure to check that the globals pointer passed in to this routine is
- not set to NIL. If the SoundOutputOpen() routine fails for any reason, the Component
- Manager will call this routine passing in a NIL for the globals.
- NOTE: This routine is never called at interrupt time.
- */
-
- PASCAL_RTN ComponentResult __SoundOutputClose (SoundOutputGlobalsPtr globals, ComponentInstance self)
- {
- if (globals != nil) // we have some globals
- {
- ReleaseHardware(globals); // make sure the hardware is off and release it
-
- if (globals->sourceComponent)
- CloseMixerSoundComponent(globals->sourceComponent); // close mixer
-
- if (globals->prefsChanged)
- ErrorMessage(SetSoundPreference(kAIFFWriterSubType, kAIFFWriterName, (Handle)globals->prefs));
-
- if (globals->inAppHeap)
- DisposeHandle((Handle)globals->prefs);
-
- if (globals->ioBuffers[0].buffer != nil)
- DisposePtr(globals->ioBuffers[0].buffer); // dispose of buffers
- if (globals->ioBuffers[1].buffer != nil)
- DisposePtr(globals->ioBuffers[1].buffer); // dispose of buffers
-
- DisposePtr((Ptr)globals); // torch our storage
- }
- return (noErr);
- }
-
-
- /* SoundOutputRegister
-
- This routine is called once, usually at boot time, when the Component Manager
- is first registering this component. This routine should check to see if the proper
- hardware is installed and return 0 if it is. If the hardware is not installed,
- the routine should return 1 and this component will not be registered. This is
- also an opportunity to do one-time initializations and perhaps register this
- component again if more than one hardware device is available. Global state information
- can also be saved in the component refcon by calling SetComponentRefCon();
-
- NOTE: The cmpWantsRegisterMessage bit must be set in the component flags of the
- component in order for this routine to be called.
- NOTE: This routine is never called at interrupt time.
- */
-
- PASCAL_RTN ComponentResult __SoundOutputRegister (SoundOutputGlobalsPtr globals)
- {
- NumVersion installedVersion;
- ComponentResult result;
- OSErr err;
-
- // we can only run if version 3.0 or greater of the Sound Manager is running;
- // we can check the entire long because the format of NumVersion is BCD data
- installedVersion = SndSoundManagerVersion();
- if (installedVersion.majorRev < 3)
- result = 1; // component doesn't want to be registered
- else
- {
- result = 0; // component wants to be registered
- err = GetPreferences(globals);
- FailWithAction(err != noErr, result = 1, Exit);
- }
-
- Exit:
- return result;
- }
-
-
- PASCAL_RTN ComponentResult __SoundOutputVersion (SoundOutputGlobalsPtr globals)
- {
- globals; // suppress "unused variable" warning for all compilers
-
- return (kSoundComponentVersion);
- }
-
-
- ///////////////////////////////////////////////////////////////////////////////////////////////////////////
- //
- // Sound component functions
- //
- ///////////////////////////////////////////////////////////////////////////////////////////////////////////
-
- /* InitOutputDevice
-
- This routine is called once when the Sound Manager first opens this component.
- The routine should initialize the hardware to default values, allocate the
- appropriate mixer component and create any other memory that is required.
-
- NOTE: This routine is never called at interrupt time.
- */
-
- PASCAL_RTN ComponentResult __SoundOutputInitOutputDevice (SoundOutputGlobalsPtr globals, long actions)
- {
- ComponentResult result;
-
- actions; // suppress "unused variable" warning for all compilers
-
- result = SetupHardware(globals);
- FailIf(result != noErr, Failure);
-
- // if any of the preferences are needed during the interrupts,
- // then locked the preferences handle now
-
- // first create a mixer and tell it the type of data it should output. The
- // description includes sample format, sample rate, sample size, number of channels
- // and the size of your optimal interrupt buffer. If a mixer cannot be found that
- // will output this type of data, an error will be returned.
-
- result = OpenMixerSoundComponent(&globals->outputData, 0, &globals->sourceComponent);
- FailIf(result != noErr, Failure);
- return (noErr);
-
- Failure:
- return (result);
- }
-
-
- /* SoundOutputGetInfo
-
- This routine returns information about this component to the Sound Manager. A
- 4-byte OSType selector is used to determine the type and size of the information
- to return. If the component does not support a selector, it should delegate this
- call on up the component chain.
-
- NOTE: This can be called at interrupt time. However, selectors that return
- a handle will not be called at interrupt time.
- */
-
- PASCAL_RTN ComponentResult __SoundOutputGetInfo (SoundOutputGlobalsPtr globals, SoundSource sourceID, OSType selector, void *infoPtr)
- {
- SoundInfoListPtr listPtr;
- UnsignedFixed *lp;
- Handle infoHandle;
- ComponentResult result;
- short *sp;
-
- result = noErr;
- switch (selector)
- {
- case siSampleSize: // return current sample size
- *((short *) infoPtr) = (**globals->prefs).sampleSize;
- break;
-
- case siSampleSizeAvailable: // return samples sizes available
- infoHandle = NewHandle(sizeof(short) * 2); // space for sample sizes
- FailWithAction(infoHandle == nil, result = MemError(), Exit);
-
- listPtr = (SoundInfoListPtr)infoPtr;
- listPtr->count = 2; // no. sample sizes in handle
- listPtr->infoHandle = infoHandle; // handle to be returned
-
- sp = (short *) *infoHandle; // store sample sizes in handle
- *sp++ = 8;
- *sp++ = 16;
- break;
-
- case siSampleRate: // return current sample rate
- *((Fixed *) infoPtr) = (**globals->prefs).sampleRate;
- break;
-
- case siSampleRateAvailable: // return sample rates available
- infoHandle = NewHandle(sizeof(UnsignedFixed) * 5); // space for sample rates
- FailWithAction(infoHandle == nil, result = MemError(), Exit);
-
- listPtr = (SoundInfoListPtr)infoPtr;
- listPtr->count = 5; // no. sample rates in handle
- listPtr->infoHandle = infoHandle; // handle to be returned
-
- // If the hardware supports a limited set of sample rates, then the list count
- // should be set to the number of sample rates and this list of rates should be
- // stored in the handle.
- lp = (UnsignedFixed *) *infoHandle;
- *lp++ = rate48khz;
- *lp++ = rate44khz;
- *lp++ = rate22050hz;
- *lp++ = rate11025hz;
- *lp++ = 0x1F400000; // 8kHz sample rate not defined
- break;
-
- case siNumberChannels: // return current no. channels
- *((short *) infoPtr) = (**globals->prefs).numChannels;
- break;
-
- case siChannelAvailable: // return channels available
- infoHandle = NewHandle(sizeof(short) * 2); // space for channels
- FailWithAction(infoHandle == nil, result = MemError(), Exit);
-
- listPtr = (SoundInfoListPtr)infoPtr;
- listPtr->count = 2; // no. channels in handle
- listPtr->infoHandle = infoHandle; // handle to be returned
-
- sp = (short *) *infoHandle; // store channels in handle
- *sp++ = 1; // mono
- *sp++ = 2; // stereo
- break;
-
- case siHardwareVolume:
- *((long *)infoPtr) = (**globals->prefs).volume;
- break;
-
- case siHardwareVolumeSteps:
- *((short *) infoPtr) = kHardwareVolumeSteps;
- break;
-
- case siHardwareMute:
- *((short *) infoPtr) = (**globals->prefs).muteState;
- break;
-
- case siHardwareBusy:
- *((short *)infoPtr) = globals->hardwareOn;
- break;
-
- // if you do not handle this selector, then delegate it up the chain
- default:
- result = SoundComponentGetInfo(globals->sourceComponent, sourceID, selector, infoPtr);
- break;
- }
-
- Exit:
- return (result);
- }
-
-
- /* SoundOutputSetInfo
-
- This routine sets information about this component. A 4-byte OSType selector is
- used to determine the type and size of the information to apply. If the component
- does not support a selector, it should delegate this call on up the component chain.
-
- NOTE: This can be called at interrupt time.
- */
-
- PASCAL_RTN ComponentResult __SoundOutputSetInfo (SoundOutputGlobalsPtr globals, SoundSource sourceID, OSType selector, void *infoPtr)
- {
- ComponentResult result;
-
- result = noErr;
- switch (selector)
- {
- case siSampleSize: // set sample size
- switch ((short)infoPtr)
- {
- case 8: // valid sample sizes
- (**globals->prefs).sampleSize = (short)infoPtr;
- globals->outputData.format = k8BitOffsetBinaryFormat;
- globals->prefsChanged = true;
- break;
-
- case 16:
- (**globals->prefs).sampleSize = (short)infoPtr;
- globals->outputData.format = k16BitBigEndianFormat;
- globals->prefsChanged = true;
- break;
-
- default:
- result = siInvalidSampleSize;
- }
- break;
-
- case siSampleRate: // set sample rate
- switch ((UnsignedFixed)infoPtr)
- {
- case rate48khz: // valid sample rates
- case rate44khz:
- case rate22050hz:
- case rate11025hz:
- case 0x1F400000:
- (**globals->prefs).sampleRate = (UnsignedFixed)infoPtr;
- globals->prefsChanged = true;
- break;
-
- default:
- result = siInvalidSampleRate;
- }
- break;
-
- case siNumberChannels: // set no. channels
- if ( ((short)infoPtr == 1) || ((short)infoPtr == 2) )
- {
- (**globals->prefs).numChannels = (short)infoPtr;
- globals->prefsChanged = true;
- }
- else
- result = notEnoughHardware;
- break;
-
- case siHardwareVolume:
- // the volume is two 16 bit values combined into a long
- // the range is 0x0000 - 0x0100, where 0x0100 is the max volume level
- (**globals->prefs).volume = (long)infoPtr;
- globals->prefsChanged = true;
- break;
-
- case siHardwareMute:
- (**globals->prefs).muteState = (short)infoPtr;
- globals->prefsChanged = true;
- break;
-
- // if you do not handle this selector, then call up the chain
- default:
- result = SoundComponentSetInfo(globals->sourceComponent, sourceID, selector, infoPtr);
- break;
- }
- return (result);
- }
-
-
- /* StartSource
-
- This routine is used to start sounds playing that are currently paused. It should
- first delegate this call up the component chain so the rest of the chain can prepare
- to play this sound. Then, if the hardware is not already started it should be
- turned on.
-
- NOTE: This can be called at interrupt time.
- */
-
- PASCAL_RTN ComponentResult __SoundOutputStartSource (SoundOutputGlobalsPtr globals, short count, SoundSource *sources)
- {
- ComponentResult result;
-
- // tell the mixer to start these sources
- result = SoundComponentStartSource(globals->sourceComponent, count, sources);
- FailIf(result != noErr, Exit);
-
- // make sure hardware interrupts are running
- StartHardware(globals);
-
- Exit:
- return (result);
- }
-
-
- /* PlaySourceBuffer
-
- This routine is used to specify a new sound to play and conditionally start
- the hardware playing that sound. It should first delegate this call up the component
- chain so the rest of the chain can prepare to play this sound. Then, if the
- hardware is not already started it should be turned on.
-
- NOTE: This can be called at interrupt time. */
-
- PASCAL_RTN ComponentResult __SoundOutputPlaySourceBuffer (SoundOutputGlobalsPtr globals, SoundSource sourceID, SoundParamBlockPtr pb, long actions)
- {
- ComponentResult result;
-
- // tell mixer to we'll start playing this new buffer
- result = SoundComponentPlaySourceBuffer(globals->sourceComponent, sourceID, pb, actions);
- FailIf(result != noErr, Exit);
-
- // if the kSourcePaused bit is set, then do not turn on your hardware just yet
- // (the assumption is that StartSource() will later be used to start this sound playing).
- // If this bit is not set, turn your hardware interrupts on.
-
- if ( !(actions & kSourcePaused) )
- StartHardware(globals);
-
- Exit:
- return (result);
- }
-